/*! 
  \file    gd32c233r_eval.c
  \brief   管理LED、按键、串口的固件函数

  \version 2025-08-08, V1.1.0, 适用于gd32c2x1的固件
*/

/*
  版权所有 (c) 2025, GigaDevice Semiconductor Inc.

  允许在满足以下条件的情况下以源代码和二进制形式重新分发和使用，无论是否经过修改：

  1. 源代码的再分发必须保留上述版权声明、本条件列表和以下免责声明。
  2. 二进制形式的再分发必须在随发行版提供的文档和/或其他材料中复制上述版权声明、本条件列表和以下免责声明。
  3. 未经事先书面许可，不得使用版权持有者或其贡献者的名称为本软件衍生的产品做推广、背书或宣传。

  本软件由版权持有者和贡献者按“原样”提供，不提供任何明示或暗示的担保，包括但不限于对适销性和特定用途适用性的暗示担保。
  在任何情况下，版权持有者或贡献者均不对因使用本软件而引起的任何直接、间接、偶然、特殊、惩罚性或后果性损害（包括但不限于采购替代商品或服务、使用损失、数据或利润损失、业务中断等）承担责任，无论是在合同诉讼、严格责任或侵权（包括疏忽或其他原因）中，即使已被告知发生此类损害的可能性。
*/

#include "gd32c231c_eval.h"

/* 私有变量 */
static const uint32_t GPIO_PORT[LEDn]           = {LED1_GPIO_PORT, LED2_GPIO_PORT, LED3_GPIO_PORT, LED4_GPIO_PORT};

static const uint32_t GPIO_PIN[LEDn]            = {LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN};

static const rcu_periph_enum COM_CLK[COMn]      = {EVAL_COM_CLK};

static const uint32_t COM_TX_PIN[COMn]          = {EVAL_COM_TX_PIN};

static const uint32_t COM_RX_PIN[COMn]          = {EVAL_COM_RX_PIN};

static const rcu_periph_enum GPIO_CLK[LEDn]     = {LED1_GPIO_CLK, LED2_GPIO_CLK, LED3_GPIO_CLK, LED4_GPIO_CLK};

static const uint32_t KEY_PORT[KEYn]            = {WAKEUP_KEY_GPIO_PORT, USER_KEY_GPIO_PORT};

static const uint32_t KEY_PIN[KEYn]             = {WAKEUP_KEY_PIN, USER_KEY_PIN};

static const rcu_periph_enum KEY_CLK[KEYn]      = {WAKEUP_KEY_GPIO_CLK, USER_KEY_GPIO_CLK};

static const exti_line_enum KEY_EXTI_LINE[KEYn] = {WAKEUP_KEY_EXTI_LINE, USER_KEY_EXTI_LINE};

static const uint8_t KEY_PORT_SOURCE[KEYn]      = {WAKEUP_KEY_EXTI_PORT_SOURCE, USER_KEY_EXTI_PORT_SOURCE};

static const uint8_t KEY_PIN_SOURCE[KEYn]       = {WAKEUP_KEY_EXTI_PIN_SOURCE, USER_KEY_EXTI_PIN_SOURCE};

static const IRQn_Type KEY_IRQn[KEYn]           = {WAKEUP_KEY_EXTI_IRQn, USER_KEY_EXTI_IRQn};

/*! 
    \brief      配置LED的GPIO
    \param[in]  lednum: 指定要配置的LED
      \arg        LED1
      \arg        LED2
      \arg        LED3
      \arg        LED4
    \param[out] 无
    \retval     无
*/
void gd_eval_led_init(led_typedef_enum lednum)
{
  /* 使能LED时钟 */
    rcu_periph_clock_enable(GPIO_CLK[lednum]);
  /* 配置LED GPIO端口 */
    gpio_mode_set(GPIO_PORT[lednum], GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN[lednum]);
    gpio_output_options_set(GPIO_PORT[lednum], GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, GPIO_PIN[lednum]);

    GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum];
}

/*! 
    \brief      点亮指定的LED
    \param[in]  lednum: 指定要点亮的LED
      \arg        LED1
      \arg        LED2
      \arg        LED3
      \arg        LED4
    \param[out] 无
    \retval     无
*/
void gd_eval_led_on(led_typedef_enum lednum)
{
    GPIO_BOP(GPIO_PORT[lednum]) = GPIO_PIN[lednum];
}

/*! 
    \brief      关闭指定的LED
    \param[in]  lednum: 指定要关闭的LED
      \arg        LED1
      \arg        LED2
      \arg        LED3
      \arg        LED4
    \param[out] 无
    \retval     无
*/
void gd_eval_led_off(led_typedef_enum lednum)
{
    GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum];
}

/*! 
    \brief      翻转指定的LED状态
    \param[in]  lednum: 指定要翻转的LED
      \arg        LED1
      \arg        LED2
      \arg        LED3
      \arg        LED4
    \param[out] 无
    \retval     无
*/
void gd_eval_led_toggle(led_typedef_enum lednum)
{
    GPIO_TG(GPIO_PORT[lednum]) = GPIO_PIN[lednum];
}

/*! 
    \brief      配置按键
    \param[in]  keynum: 指定要配置的按键
      \arg        KEY_WAKEUP: 唤醒按键
      \arg        KEY_TAMPER: 防拆按键
    \param[in]  keymode: 指定按键模式
      \arg        KEY_MODE_GPIO: 按键作为普通IO使用
      \arg        KEY_MODE_EXTI: 按键连接到带中断的EXTI线
    \param[out] 无
    \retval     无
*/
void gd_eval_key_init(key_typedef_enum keynum, keymode_typedef_enum keymode)
{
  /* 使能按键时钟 */
    rcu_periph_clock_enable(KEY_CLK[keynum]);
    rcu_periph_clock_enable(RCU_SYSCFG);

  /* 配置按键引脚为输入 */
    gpio_mode_set(KEY_PORT[keynum], GPIO_MODE_INPUT, GPIO_PUPD_NONE, KEY_PIN[keynum]);

    if(keymode == KEY_MODE_EXTI) {
  /* 使能并设置按键EXTI中断为1优先级 */
  nvic_irq_enable(KEY_IRQn[keynum], 1U);
  /* 连接按键EXTI线到按键GPIO引脚 */
  syscfg_exti_line_config(KEY_PORT_SOURCE[keynum], KEY_PIN_SOURCE[keynum]);
  /* 配置按键EXTI线 */
  exti_init(KEY_EXTI_LINE[keynum], EXTI_INTERRUPT, EXTI_TRIG_RISING);
  exti_interrupt_flag_clear(KEY_EXTI_LINE[keynum]);
    }
}

/*! 
    \brief      获取指定按键的状态
    \param[in]  keynum: 指定要检测的按键
      \arg        KEY_WAKEUP: 唤醒按键
      \arg        KEY_TAMPER: 防拆按键
    \param[out] 无
    \retval     按键GPIO引脚的值
*/
uint8_t gd_eval_key_state_get(key_typedef_enum keynum)
{
    return gpio_input_bit_get(KEY_PORT[keynum], KEY_PIN[keynum]);
}

/*! 
    \brief      配置板载串口
    \param[in]  com: 板载串口
      \arg        EVAL_COM: 板载串口
    \param[out] 无
    \retval     无
*/
void gd_eval_com_init(uint32_t com)
{
    uint32_t com_id = 0U;

    if(EVAL_COM == com) {
        com_id = 0U;
    } else {
        while(1);
    }

  /* 使能串口GPIO时钟 */
    rcu_periph_clock_enable(EVAL_COM_GPIO_CLK);

  /* 使能USART时钟 */
    rcu_periph_clock_enable(COM_CLK[com_id]);

  /* 连接端口到USARTx_Tx */
    gpio_af_set(EVAL_COM_GPIO_PORT, EVAL_COM_AF, COM_TX_PIN[com_id]);

  /* 连接端口到USARTx_Rx */
    gpio_af_set(EVAL_COM_GPIO_PORT, EVAL_COM_AF, COM_RX_PIN[com_id]);

  /* 配置USART Tx为复用推挽输出 */
    gpio_mode_set(EVAL_COM_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, COM_TX_PIN[com_id]);
    gpio_output_options_set(EVAL_COM_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, COM_TX_PIN[com_id]);

  /* 配置USART Rx为复用推挽输出 */
    gpio_mode_set(EVAL_COM_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, COM_RX_PIN[com_id]);
    gpio_output_options_set(EVAL_COM_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, COM_RX_PIN[com_id]);

  /* 配置USART */
    usart_deinit(com);
    usart_baudrate_set(com, 115200U);
    usart_receive_config(com, USART_RECEIVE_ENABLE);
    usart_transmit_config(com, USART_TRANSMIT_ENABLE);
    usart_enable(com);
}

#ifdef GD_ECLIPSE_GCC
/* 在Eclipse GCC环境下重定向C库的printf函数到USART */
int __io_putchar(int ch)
{
    usart_data_transmit(EVAL_COM, (uint8_t) ch );
    while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
    return ch;
}
#elif (defined (__ICCARM__) && (__VER__ >= 9000000))
#include <LowLevelIOInterface.h>

#pragma module_name = "?__write"

int MyLowLevelPutchar(int x)
{
     usart_data_transmit(EVAL_COM, (uint8_t) x);
     while (RESET == usart_flag_get(EVAL_COM,USART_FLAG_TBE));
     return x;
 
}

/* 在IAR __VER__ >= 9000000环境下重定向C库的printf函数到USART */
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
  /* Remove the #if #endif pair to enable the implementation */
#if 1
  size_t nChars = 0;

  if (buffer == 0)
  {
    /*
     * This means that we should flush internal buffers.  Since we
     * don't we just return.  (Remember, "handle" == -1 means that all
     * handles should be flushed.)
     */
    return 0;
  }

  /* This template only writes to "standard out" and "standard err",
   * for all other file handles it returns failure. */
  if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR)
  {
    return _LLIO_ERROR;
  }

  for (/* Empty */; size != 0; --size)
  {
    if (MyLowLevelPutchar(*buffer++) < 0)
    {
      return _LLIO_ERROR;
    }

    ++nChars;
  }

  return nChars;

#else

  /* Always return error code when implementation is disabled. */
  return _LLIO_ERROR;

#endif

}
#else
/* 重定向C库的printf函数到USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(EVAL_COM, (uint8_t)ch);
    while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));

    return ch;
}
#endif /* GD_ECLIPSE_GCC */
